(* Author Esther Kundin & Ayla Brayer *)

(* binary operators *)
type op = Add | Sub | Mult | Div | Equal | Neq 
         | Less | Leq | Greater |Geq | And | Or

(* expressions *)
type exprn =
    Literal of int
  | Str of string
  | Id of string
  | Float of float
  | Bool of bool 
  | Assign of string * exprn
  | Binop of exprn * op * exprn
  | IntNegation of int
  | FloatNegation of float 
  | IdNegation of string
  | Exprn of exprn
  | Noexpr 

(* the type of a variable is stored as a string *)
type vartype = string 

(* 
a statement can be either a print or expression.  since we don't have
any function calls in our langage, it was easier to represent the 
print directly in the tree rather than make it a special case in the
interpretation layer
*)
type statement = 
    Print of string
   | Expr of exprn

(* we have different types of variable declarations *)
type var_decleration = 
    IntDeclaration of string * string * int 
|   FloatDeclaration of string * string * float 
|   StringDeclaration of string * string * string
|   BoolDeclaration of string * string * bool 

(* statement lists are used in the answer block or footer *)
type statement_list = statement list

(* 
Since questions and answers are nested, need to be declated
with "and" so that they can refer to eatch other
*)
type question = string * vartype * answer_list
and answer = exprn * answer_block
and answer_block =  
    Block of statement_list
   |AnswBlock of statement_list * question
   |Repeat of statement_list
and answer_list = answer list

(* Top-level program *)
type program = var_decleration list * question list * statement list


(*********************************************
 Below are all the print function 
    for debugging/display of the ast
**********************************************)

(* print an expression *)	
let rec string_of_expr = function
    Literal(l) -> string_of_int l
  | Id(s) -> s
  | Str (s) -> s
  | Float (s) -> string_of_float s
  | Binop(e1, o, e2) ->
      string_of_expr e1 ^ " " ^
      (match o with
        Add -> "+"
      | Sub -> "-"
      | Mult -> "*" 
      | Div -> "/"
      | Equal -> "==" 
      | Neq -> "!=" 
      | And -> "&" 
      | Or -> "|"
      | Less -> "<" 
      | Leq -> "<=" 
      | Greater -> ">" 
      | Geq -> ">=") ^ " " ^
      string_of_expr e2
  | Bool (s) -> string_of_bool s   
  | Assign(v, e) -> v ^ " = " ^ string_of_expr e 
  | IntNegation (i) -> "(-" ^ string_of_int i ^ ")"
  | FloatNegation (i) -> "(-" ^ string_of_float i ^ ")"
  | IdNegation (i) -> "(-" ^ i ^ ")"
  | Exprn (e) -> string_of_expr e
  | Noexpr -> ""


(* print variable decleration *)
let string_of_var_decleration = function
   IntDeclaration (t,i,v) -> t ^ " " ^ i ^ " " ^ " = " ^
                             string_of_int v ^ ";\n"
|  StringDeclaration (t,i,v) -> t ^ " " ^ i ^ " " ^ " = " ^ v ^ ";\n"
|  FloatDeclaration (t,i,v) -> t ^ " " ^ i ^ " " ^ " = " ^
                               string_of_float v ^ ";\n"
|  BoolDeclaration  (t,i,v) ->  t ^ " " ^ i ^ " " ^ " = " ^
                                string_of_bool v ^ ";\n"


(* print a statement *)   
let rec string_of_statement = function 
   Print (str) -> "print" ^ "(" ^ str ^ ");\n"
|  Expr (e) -> string_of_expr e ^ ";\n"
(* print an answer *)
and string_of_answer = function 
	(e, ans_block) -> "condition: if(" ^ string_of_expr e ^ ")\n" ^
                     string_of_answer_block ans_block
(* print a question *)	 
and string_of_question =  function 
     (str, tp, ans) ->  "" ^ str ^ "(" ^ tp ^ ")\n" ^ 
				     String.concat "\n" (List.map string_of_answer ans)
(* print an answer_block *)
and string_of_answer_block = function
     Block (sl) -> 
        "" ^ String.concat "" (List.map string_of_statement sl)
   | AnswBlock (sl, q) -> 
      String.concat "\n" (List.map string_of_statement sl) ^ 
      "\n" ^ string_of_question q
   | Repeat (sl) -> 
      String.concat "\n" (List.map string_of_statement sl) ^
      "Repeat;\n" 
        
(* print a qaq program devided to 3 parts *)
let string_of_program (vars , qs, statem ) =
  "DECLERATIONS:\n" ^ String.concat "" (
     List.map string_of_var_decleration (List.rev vars) ) 
	 ^ "\n" ^
  "QUESTIONS:\n" ^ String.concat "\n" (
     List.map string_of_question (List.rev qs)) ^ "\n" ^  
  "STATEMENTS:\n" ^String.concat "" (
     List.map string_of_statement (List.rev statem) ) ^ "\n"
